#include "axl_framework.h"

using namespace AXL_Projects;

static volatile int TimerFrameCount; 	//the accumulated fps - updated every frame after drawing - for the CurrentFPS
volatile int TimerCurrentFPS;	//fps value to report as the fPS - updated every second from TimerFrameCount
static volatile int TimerGameTime;		//current value for gametimer for keeping the logic in sync with the time
static volatile bool TimerDigiPurge;	//indicates if sound samples need purging

Framework::Framework(const std::string& configfilename,const std::string& animationfilename, bool initialiseSystem, BITMAP* (*bitmapcreator)(const std::string& ))
{
	//set up framework and get us into a stable allegro graphical state
	Configuration::GlobalErrorString="";
	this->GameAnimationLibrary=NULL;
	this->GameConfiguration=NULL;

	//first do configuration
	Configuration::LogEntry("Framework initialising with both XML files",true);
	GameConfiguration=new Configuration(configfilename);

	//initialise any graphic loading callback functions
	BitmapCreator=bitmapcreator;

	if(BitmapCreator) Configuration::LogEntry("Framework using a custom bitmap loader");
	else Configuration::LogEntry("Framework using built in bitmap loader");

	AnimationFile=animationfilename;
	if(initialiseSystem)
		InitialiseFramework();
}

void Framework::InitialiseFramework()
{
	int r;
	bool ret=false;
	DrawingSurface=NULL;
	BitmapPages[0]=BitmapPages[1]=BitmapPages[2]=NULL;

	for(int i=0;i<255;i++) VoiceArray[i]=-1;

	if(Configuration::GlobalErrorString!="")
	{
		if(!GameConfiguration) delete GameConfiguration;
		GameConfiguration=NULL;
		Configuration::LogEntry("Initialisation of configuration XML failed.");
		allegro_message("Initialisation of configuration XML failed. See .log files\n%s",Configuration::GlobalErrorString.c_str());
	}
	else
	{
		r=GameConfiguration->AllegroStart();	//start up allegro and finish in a graphics mode
		
		//will only fail on system or graphics initialisation
		if(r==0 || Configuration::GlobalErrorString!="")
		{
			if(!GameConfiguration) delete GameConfiguration;
			GameConfiguration=NULL;
			Configuration::LogEntry("Initialisation of allegro graphics failed");
			allegro_message("Initialisation of allegro graphics system failed. See .log files\n%s",Configuration::GlobalErrorString.c_str());
		}
		else
		{
			Configuration::LogEntry("framework initialised and in graphics mode");
			ret=InitialiseSubSystem();

			if(ret)
			{
				//animation
				if(AnimationFile!="")
				{
					BitmapType maxtype;
					Configuration::LogEntry("Loading animations",false);
					if(!GameConfiguration->CapsGraphics.CapGraphicsToBufferType)
					{
						Configuration::LogEntry("Maximum graphics type for loaded graphics/animations will be VIDEO as no capping is active");
						maxtype=TYPE_VIDEO;
					}
					else
					{
						GraphicsRenderMode rm=GameConfiguration->CapsActualSystem.GraphicsMode;
						switch(rm)
						{
							case MODE_TRIPLE:
								maxtype=TYPE_VIDEO;
								Configuration::LogEntry("Maximum graphics type for loaded graphics/animations will be VIDEO");
								break;
							case MODE_PAGED:
								Configuration::LogEntry("Maximum graphics type for loaded graphics/animations will be VIDEO");
								maxtype=TYPE_VIDEO;
								break;
							case MODE_SYSTEMDOUBLE:
								Configuration::LogEntry("Maximum graphics type for loaded graphics/animations will be SYSTEM");
								maxtype=TYPE_SYSTEM;
								break;
							default:
							//MODE_DOUBLE:
								Configuration::LogEntry("Maximum graphics type for loaded graphics/animations will be MEMORY");
								maxtype=TYPE_MEMORY;
								break;
						}
					}							
					this->GameAnimationLibrary=new AnimationLibrary(AnimationFile, GameConfiguration->CapsSystem.fps,maxtype,BitmapCreator);
					if(!ret) 
					{
						Configuration::LogEntry("Failed loading animations. See other errors");
						Configuration::GlobalErrorString="Failed loading animations. See logs";
					}
				}
				else
					Configuration::LogEntry("No animation file to load");

			}
			if(GameConfiguration->CapsActualSystem.GraphicsMode>MODE_SYSTEMDOUBLE)
				Configuration::LogEntry("WARNING: Unless patched (or version >4.2) and sheet graphics are used, they will fail to display properly");				
		}
	}
	
	if( !ret)
	{
		Configuration::GlobalErrorString="Failed Framework Constructor. See errors in log files.";
		Configuration::LogEntry("--------------------------------");
		Configuration::LogEntry("Framework initialisation failed");
		Configuration::LogEntry("--------------------------------");
	}
	else
	{
		Configuration::LogEntry("--------------------------------");
		Configuration::LogEntry("Framework initialisation success");
		Configuration::LogEntry("--------------------------------");
	}

}

void Framework::ChangeTimerType(TimerType activeTimerType)
{
	this->ActiveTimerSwitch=true;
	this->ActiveTimerType=activeTimerType;
	TimerGameTime=1;
}

void Framework::DestroyFramework(bool fromdestructor)
{
	//must duplicate stuff here into abortsystem
	//remove configuration object
	bool setgfx=false;

	if(fromdestructor) Configuration::LogEntry("Destroying Framework From Destructor");		
	else Configuration::LogEntry("Destroying Framework From Code Call");
	if(GameConfiguration)
	{
		Configuration::LogEntry("Configuration object being deleted");		
		setgfx=GameConfiguration->AllegroInitialised;
		delete GameConfiguration;
		GameConfiguration=NULL;
	}
	else
		Configuration::LogEntry("Configuration not being deleted");			

	if(GameAnimationLibrary)
	{
		//if a sheet master is system bitmap then deleting any subbitmaps will crash the system
		Configuration::LogEntry("Animation object being deleted");		
		delete GameAnimationLibrary;
		GameAnimationLibrary=NULL;
	}
	else
		Configuration::LogEntry("Animation not being deleted");			

	//delete any used up stuff
	int i=0;
	DrawingSurface=NULL;
	for(i=0;i<3;++i)
	{
		if(BitmapPages[i])
		{
			Configuration::LogEntry("freeing graphic buffer");			
			destroy_bitmap(BitmapPages[i]);
			BitmapPages[i]=NULL;
		}
		else
			Configuration::LogEntry("not freeing graphic buffer as null. This is normal.");
	}
//uninstall timers
	if(TimersActive) 
	{
#if (OPTIMISE_NO_FPS==0)
		remove_int(TimerFpsHandler);
#endif
		remove_int(TimerGameTimer);
		TimersActive=false;
	}
	if(DigiPurgeActive)
	{
		DigiPurgeActive=false;
		remove_int(TimerDigiPurgeNow);
	}
	
	if(setgfx) set_gfx_mode(GFX_TEXT,640,480,0,0);
}

Framework::~Framework()
{
	DestroyFramework(true);
		
}

bool Framework::InitialiseSubSystem()
{
	//this sets up the framework bitmaps and other graphics related things
	//stuff that is only required when running in looped mode, e.g. timers is not set here, but in the gameloop
	// function so that the timers aren't started until actually required
	// configuration object already created
	AutoLogic=AutoDraw=NULL;
	AutoOverride=false;
	TimersActive=false;
	DigiPurgeActive=false;
	BitmapPagesActive=0;

	//create bitmaps here
	//temp for double buffer at the minute
	assert(GameConfiguration);
	if(BitmapPages[0]) destroy_bitmap(BitmapPages[0]);
	if(BitmapPages[1]) destroy_bitmap(BitmapPages[1]);
	if(BitmapPages[2]) destroy_bitmap(BitmapPages[2]);

	bool CanDoIt=false;
	switch(GameConfiguration->CapsGraphics.GraphicsMode)
	{
		case MODE_TRIPLE:
			if(gfx_capabilities & GFX_CAN_TRIPLE_BUFFER) CanDoIt=true;
			else CanDoIt=(enable_triple_buffer()==0);

			if(CanDoIt) 
			{
				Configuration::LogEntry("System Wanted and can do Triple buffering");
				BitmapPages[0]=create_video_bitmap(GameConfiguration->CapsGraphics.ScrWidth,GameConfiguration->CapsGraphics.ScrHeight);
				BitmapPages[1]=create_video_bitmap(GameConfiguration->CapsGraphics.ScrWidth,GameConfiguration->CapsGraphics.ScrHeight);
				BitmapPages[2]=create_video_bitmap(GameConfiguration->CapsGraphics.ScrWidth,GameConfiguration->CapsGraphics.ScrHeight);
				if(!BitmapPages[0] || !BitmapPages[1] || !BitmapPages[2])
				{
					Configuration::LogEntry("Failed creating the 3 video bitmaps for Triple Buffering. Trying Paging");
					if(BitmapPages[0]) destroy_bitmap(BitmapPages[0]);
					if(BitmapPages[1]) destroy_bitmap(BitmapPages[1]);
					if(BitmapPages[2]) destroy_bitmap(BitmapPages[2]);
					BitmapPages[0]=BitmapPages[1]=BitmapPages[2]=NULL;
					//then fall through to paged
					CanDoIt=false;
				}
				else
				{
					clear_to_color(BitmapPages[0],makecol(0,0,0));
					clear_to_color(BitmapPages[1],makecol(0,0,0));
					clear_to_color(BitmapPages[2],makecol(0,0,0));
					Configuration::LogEntry("Got Triple Buffering");
					GameConfiguration->CapsActualSystem.GraphicsMode=MODE_TRIPLE;
				}
			}
			else
				Configuration::LogEntry("Wanted Triple but System reported it cannot. Trying Paged");

			if(CanDoIt)
				break;
		case MODE_PAGED:
			BitmapPages[0]=create_video_bitmap(GameConfiguration->CapsGraphics.ScrWidth,GameConfiguration->CapsGraphics.ScrHeight);
			BitmapPages[1]=create_video_bitmap(GameConfiguration->CapsGraphics.ScrWidth,GameConfiguration->CapsGraphics.ScrHeight);
			BitmapPages[2]=NULL;
			if(!BitmapPages[0] || !BitmapPages[1])
			{
				Configuration::LogEntry("Failed creating the 2 video bitmaps for Paged Buffering. Trying Double");
				if(BitmapPages[0]) destroy_bitmap(BitmapPages[0]);
				if(BitmapPages[1]) destroy_bitmap(BitmapPages[1]);
				BitmapPages[0]=BitmapPages[1]=BitmapPages[2]=NULL;
				//then fall through to system
				CanDoIt=false;
			}
			else
			{
				clear_to_color(BitmapPages[0],makecol(0,0,0));
				clear_to_color(BitmapPages[1],makecol(0,0,0));
				Configuration::LogEntry("Got Paged Buffering");
				GameConfiguration->CapsActualSystem.GraphicsMode=MODE_PAGED;
				CanDoIt=true;
			}
			if(CanDoIt)
				break;
		case MODE_SYSTEMDOUBLE:
			BitmapPages[0]=create_system_bitmap(GameConfiguration->CapsGraphics.ScrWidth,GameConfiguration->CapsGraphics.ScrHeight);
			BitmapPages[1]=BitmapPages[2]=NULL;
			//should always be a bitmap as system falls back to memory bitmap if not available
			if(BitmapPages[0])
			{
				if(is_system_bitmap(BitmapPages[0]))
				{
					GameConfiguration->CapsActualSystem.GraphicsMode=MODE_SYSTEMDOUBLE;
					clear_to_color(BitmapPages[0],makecol(0,0,0));
					Configuration::LogEntry("Got System Double Buffering.");
					CanDoIt=true;
					break;
				}
				else
				{
					destroy_bitmap(BitmapPages[0]);
					BitmapPages[0]=NULL;
					Configuration::LogEntry("Failed creating System bitmap. Trying Double");
					CanDoIt=false;
				}
			}
			else
				Configuration::LogEntry("Failed to get a System, trying double");
		default:
			//double - DR needs to be done by user
			Configuration::LogEntry("Using double buffering");
			BitmapPages[0]=create_bitmap(GameConfiguration->CapsGraphics.ScrWidth,GameConfiguration->CapsGraphics.ScrHeight);
			if(BitmapPages[0])
			{
				GameConfiguration->CapsActualSystem.GraphicsMode=MODE_DOUBLE;
				Configuration::LogEntry("Got Double Buffering");
				CanDoIt=true;
			}
			else
				Configuration::LogEntry("Failed to get a Double Buffer. Crikey!");
	}

	assert(CanDoIt);

	Configuration::LogEntry("Sub System Finished initialising");

	if(GameConfiguration->CapsActualSystem.GraphicsMode==GameConfiguration->CapsGraphics.GraphicsMode)
		Configuration::LogEntry("The graphics mode the system got was the one the configuration wanted");
	else
		Configuration::LogEntry("The graphics mode the system got was not the one the configuration wanted");

	this->DrawingSurface=BitmapPages[0];

	return CanDoIt;
}

bool Framework::StartGameLoop(	
								bool (*logiclogo)(void),
								bool (*drawinglogo)(void),
								bool (*logicmenu)(void),
								bool (*drawingmenu)(void),
								bool (*logicgame)(void),
								bool (*drawinggame)(void),
								bool (*logicexit)(void),
								bool (*drawingexit)(void),
								bool bUseEscToExit
						)
{
	//set function pointers and start timers
	int r;
	assert(GameConfiguration && GameConfiguration->AllegroInitialised);
	
	TimerCurrentFPS=TimerFrameCount=TimerGameTime=0;
	LOCK_VARIABLE(TimerFpsCount);
	LOCK_VARIABLE(TimerFrameCount);
	LOCK_VARIABLE(TimerGameTime);
	LOCK_VARIABLE(TimerDigiPurge);
	LOCK_FUNCTION(TimerFpsHandler);
	LOCK_FUNCTION(TimerDigiPurgeNow);
	LOCK_FUNCTION(TimerGameTimer);

	//install timers for the game loop and the FPS check
	//fps updated ever second
	TimersActive=DigiPurgeActive=false;
	
#if (OPTIMISE_NO_FPS==0)
	r=install_int(TimerFpsHandler,1000);
#else
	Configuration::LogEntry("FPS timer optimised out. no FPS indicator available.");		
	r=0;
#endif
	//game timer required FPS taken from configuration - animation object if different to this will take this value
	if(r==0) 
	{
#if (OPTIMISE_NO_FPS==0)
		Configuration::LogEntry("FPS timer created");		
#endif
		r=install_int_ex(TimerGameTimer,BPS_TO_TIMER(GameConfiguration->CapsSystem.fps));
		if(r!=0) 
		{
#if (OPTIMISE_NO_FPS==0)
			remove_int(TimerFpsHandler);
#endif
			Configuration::LogEntry("unable to create game timer. fps timer removed")	;		
		}			
		else 
		{
			TimersActive=true;
			Configuration::LogEntry("game timer created");			
		}
	}	
	else
		Configuration::LogEntry("unable to create fps timer");	
	
	//install sound purge timer if sound or midi is available.
	if(r==0 && (GameConfiguration->CapsActualSystem.UseMidi || GameConfiguration->CapsActualSystem.UseSound))
	{
		r=install_int(TimerDigiPurgeNow,TIMER_DIGIPURGETIME);
		if(r==0) 
		{
			Configuration::LogEntry("digi purge timer created");	
			DigiPurgeActive=true;
		}
		else
		{
			Configuration::LogEntry("unable to create digi timer, removing all others");	
#if (OPTIMISE_NO_FPS==0)
			remove_int(TimerFpsHandler);
#endif
			remove_int(TimerGameTimer);
			TimersActive=false;
			DigiPurgeActive=false;
		}
	}	
		
	if(r!=0)
	{
		Configuration::LogEntry("unable to create timers");			
		Configuration::GlobalErrorString="unable to create timers";
		return false;
	}
	
	if(logiclogo==NULL && logicmenu==NULL && logicgame==NULL && logicexit==NULL && drawinglogo==NULL && drawingmenu==NULL && drawinggame==NULL && drawingexit==NULL)
	{
			Configuration::LogEntry("no function pointers passed in! removing timers");
			Configuration::GlobalErrorString="no function pointers set in game loop!";
			if(TimersActive)
			{
#if (OPTIMISE_NO_FPS==0)
				remove_int(TimerFpsHandler);
#endif
				remove_int(TimerGameTimer);
			}
			if(DigiPurgeActive)
				remove_int(TimerDigiPurgeNow);

			TimersActive=DigiPurgeActive=false;	
			return false;
	}

	Configuration::LogEntry("-----------------------------------------------");	
	Configuration::LogEntry("Framework sub-system success. Calling game loop");	
	Configuration::LogEntry("-----------------------------------------------");	

	//run forever loop until exit	
	GameLoop(					logiclogo,
								drawinglogo, 
								logicmenu, 
								drawingmenu, 
								logicgame, 
								drawinggame, 
								logicexit, 
								drawingexit, 
								bUseEscToExit
	);
	Configuration::LogEntry("-----------------------------------------------");	
	Configuration::LogEntry("Gameloop ended. Returning back to calling program");	
	Configuration::LogEntry("-----------------------------------------------");	
	return true;
}

//set up auto game loop if required
//otherwise coder makes his own timer loop
//called by the gameloop public
void Framework::GameLoop(	
						bool (*logiclogo)(void),
						bool (*drawinglogo)(void),
						bool (*logicmenu)(void),
						bool (*drawingmenu)(void),
						bool (*logicgame)(void),
						bool (*drawinggame)(void),
						bool (*logicexit)(void),
						bool (*drawingexit)(void),
						bool useESCtoExit
						)
{

	//graphic mode on start and graphic mode on exit
	bool exitgame=false;
	bool draw=false;
	//if this is called then we are in the main loop implementing our own game time
	//basically a standard timer based loop is implemented using any of the passed
	//in function pointers if not NULL
	//if any of the above return false then the game loop exits
	//if none set up then ESCape key is automatically implemented to exit the game

	assert(GameConfiguration && GameConfiguration->AllegroInitialised && TimersActive);

	clear_keybuf();
	TimerDigiPurge=false;
	TimerGameTime=0;
	TimerFrameCount=TimerCurrentFPS=0;
	
	//set up default mode
	//each mode should set the next mode when it is ready to finish/start
	if(logiclogo!=NULL || drawinglogo!=NULL) ActiveTimerType=GAME_INTRO;
	else if(logicmenu!=NULL || drawingmenu!=NULL) ActiveTimerType=GAME_MENU;
	else if(logicgame!=NULL || drawinggame!=NULL) ActiveTimerType=GAME_GAME;
	else if(logicexit!=NULL || drawingexit!=NULL) ActiveTimerType=GAME_EXIT;

	if(useESCtoExit)
		Configuration::LogEntry("ESCape will abort immediately");
	else
		Configuration::LogEntry("ESCape not set to abort immediately");

	if(!exitgame)
	{
		do {
			while(TimerGameTime!=0) 
			{
				//CurrentTimerType=ActiveTimerType;
				draw=false;
				//logic code

				//if using auto override hooks then call these
				//return of true means exit override
				//also set to false if call autooverride with nulls
				
				//reset timer switch as we've been round once
				//this is there to stop calling wrong draw - ensure correct logic before a draw
				ActiveTimerSwitch=false;
				if(this->AutoOverride)
				{
					if(AutoLogic())
						AutoOverride=false;	
				}
				//cannot be an else because when autologic returns true
				//it is reset but the logic hasn't been called
				//so could call drawing without logic which may be bad
				//but shouldn't be because override should preserve the previous mode data
				//usually!
				if(!AutoOverride)
				{
					switch(ActiveTimerType)
					{
					case GAME_INTRO:
						exitgame=logiclogo();
						break;
					case GAME_MENU:
						exitgame=logicmenu();
						break;
					case GAME_GAME:
						exitgame=logicgame();
						break;
					case GAME_EXIT:
						exitgame=logicexit();
						break;
					}
				}

				if(!exitgame && !ActiveTimerSwitch)  {
				//if(!exitgame)  {
					draw=true;
				}
				TimerGameTime--;
				if(useESCtoExit && key[KEY_ESC]) 
					exitgame=true;
					
			}
			//should exit loop every FRAMERATE/second second if on time
			//update display if frame has been drawn
			//if not then system is too fast so don't do anything	
			//only draw if the same timer type, i.e. if switched during logic
			//then need to ignore this loop until the next one
			//if(draw && !exitgame && CurrentTimerType==ActiveTimerType)
			if(draw && !exitgame)
			{
				//only call lock if not a video bitmap
				if (GameConfiguration->CapsActualSystem.GraphicsMode!=MODE_PAGED && GameConfiguration->CapsActualSystem.GraphicsMode!=MODE_TRIPLE) 
					acquire_bitmap(DrawingSurface);

				//function call order is z order
				if(this->AutoOverride)
				{
					if(AutoDraw())
						AutoOverride=false;
				}
				else
				{
					switch(ActiveTimerType)
					{
					case GAME_INTRO:
						exitgame=drawinglogo();
						break;
					case GAME_MENU:
						exitgame=drawingmenu();
						break;
					case GAME_GAME:
						exitgame=drawinggame();
						break;
					case GAME_EXIT:
						exitgame=drawingexit();
						break;
					}
				}
				if (GameConfiguration->CapsActualSystem.GraphicsMode!=MODE_PAGED && GameConfiguration->CapsActualSystem.GraphicsMode!=MODE_TRIPLE)
					release_bitmap(DrawingSurface);

				//set correct buffer
				//double/system is always pointing at one and only page
				//need to modify if using dirty.
				if(this->GameConfiguration->CapsActualSystem.GraphicsMode<=MODE_SYSTEMDOUBLE)
				{
					//double buffering to either a memory or a system bitmap
					if(GameConfiguration->CapsGraphics.VSync) vsync();
						blit(DrawingSurface, screen, 0, 0, 0, 0,GameConfiguration->CapsGraphics.ScrWidth,GameConfiguration->CapsGraphics.ScrHeight);
				}
				else
				{
					//release_bitmap(DrawingSurface);
					if(this->GameConfiguration->CapsActualSystem.GraphicsMode==MODE_PAGED)
					{
						show_video_bitmap(BitmapPages[BitmapPagesActive]);
						//show_video_bitmap(DrawingSurface);
						BitmapPagesActive=1-BitmapPagesActive;
						DrawingSurface=BitmapPages[BitmapPagesActive];
					}
					else
					{
						//triple
						do{}while(poll_scroll());
						request_video_bitmap(BitmapPages[BitmapPagesActive]);
						//request_video_bitmap(DrawingSurface);
						BitmapPagesActive=(BitmapPagesActive+1)%3;
						DrawingSurface=BitmapPages[BitmapPagesActive];
					}
				}

#if (OPTIMISE_NO_FPS==0)
				TimerFrameCount++; //update every drawn screen - should match fps which is required FPS or less, cannot be higher
#endif
				draw=false;
			}
			//sound routine
			if(TimerDigiPurge==true) 
			{
				TimerDigiPurge=false;
				SoundPurgeDigi();
			}

		} while(exitgame==false);
	}

	Configuration::LogEntry("Game Loop Ended");			
}

void Framework::SetAutoGameLoopOverride(
	bool (*logic)(void),
	bool (*drawing)(void) )
{

	if(logic==NULL && drawing==NULL)
	{
		this->AutoOverride=false;
	}
	else
	{
		TimerGameTime=1;
		ActiveTimerSwitch=true;
		AutoOverride=true;
		AutoLogic=logic;
		AutoDraw=drawing;
	}
}

bool Framework::RestartSystem(void)
{
	//restart system
	//aborts if fail
	//but passes back fail if animations exist but not initialised via a XML file
	//- animation constructors are not supported for this method
	//regarding the calling program:
	//all pointers to parts of the configuration, animation and framework must be deleted
	//all animations, etc. must be destroyed
	
	//1. delete all animations
	//2. delete all framework buffers
	//3. reinitialise framework stuff
	//4. restart configuration
	//5. load all graphics
	
	Configuration::GlobalErrorString="";
	
	Configuration::LogEntry("********* Restarting System. Brace yourself (crash possible on system sub-bitmaps)");
	//animations
	if(GameAnimationLibrary)
	{
		if(AnimationFile=="")
		{
			Configuration::LogEntry("TRIED TO RESTART BUT ANIMATION NOT SET BY CONFIG FILE/BUFFER SO CANNOT RELOAD. FAILING");
			return false;
		}
		else
		{
			delete GameAnimationLibrary;
			GameAnimationLibrary=NULL;
		}
	}
	Configuration::LogEntry("********* Animation unloaded");
	//framework
	//delete any used up stuff
	int i=0;
	DrawingSurface=NULL;
	for(i=0;i<3;++i)
	{
		if(BitmapPages[i])
		{
			destroy_bitmap(BitmapPages[i]);
			BitmapPages[i]=NULL;
		}
	}
	Configuration::LogEntry("********* Buffers destroyed");

	//restart - this starts up configuration and animation files
	InitialiseFramework();
	
	if(Configuration::GlobalErrorString!="")
		this->AbortSystem("Failed re-initialising! See log");
		
	Configuration::LogEntry("********* Re-initialised");
	return true;
}


/////////////////////////////////////////////////////
// public helpers
void Framework::AbortSystem(const std::string& err)
{
	std::ostringstream ss;
	
	//abort system, do some quick tidying
	if(GameConfiguration)
		delete GameConfiguration;
	if(GameAnimationLibrary)
		delete GameAnimationLibrary;
	
	int i=0;
	for(i=0;i<3;i++)
	{
		if(BitmapPages[i])
			destroy_bitmap(BitmapPages[i]);
	}

	if(TimersActive) 
	{
#if (OPTIMISE_NO_FPS==0)
		remove_int(TimerFpsHandler);
#endif
		remove_int(TimerGameTimer);
	}
	if(DigiPurgeActive)
		remove_int(TimerDigiPurgeNow);

	set_gfx_mode(GFX_TEXT,0,0,0,0);
	if((err!="")) 
		ss << "Aborting system because: " << err;
	else 
		ss << "Aborting system (no reason given)";

	allegro_message(ss.str().c_str());
	allegro_exit();
	exit(0);
	
}

bool Framework::DatLoad(DATAFILE** dat, const std::string& file, bool stop) 
{
	//load a dat file
	//	handy as caters for errors
	//	re-opens a file if it is already open
	//		must be set to NULL if new file
	//	and optional abort message if failed
    //	useful if change modes
    Configuration::LogEntry("Loading .dat file '"+file+"'");
	if(*dat!=NULL) {
      unload_datafile(*dat);
    }
	*dat=NULL;
	
    //try to open it
    *dat=load_datafile(file.c_str());

    //abort if in error
    if(stop && (*dat)==NULL) 
	{
    	Configuration::LogEntry("dat file load failed and aborting");
    	Configuration::GlobalErrorString="Loading .dat file '"+file+"' failed. aborting";
        AbortSystem("The data file '" + file + "' could not be loaded");
    }
    
    if((*dat)==NULL)
    {
    	Configuration::GlobalErrorString="Loading .dat file '"+file+"' failed. aborting";
    	return false;
	}
	else
		return true;
}

void Framework::MsgBox(const char* msg1,const char* msg2,BITMAP* bmp,int keytowaitfor)
{
	//all parameters are optional except line1
	//change defaults as necessary
	if(!GameConfiguration->AllegroInitialised) allegro_message(msg1);
	else 
		MsgBox(msg1,msg2,bmp,(int)bmp->w,(int)bmp->h,makecol(255,0,0),makecol(200,200,200),true,keytowaitfor);
}

void Framework::MsgBox(const char* line1,const char* line2, BITMAP* bmp, int w,int h,int transcolour, int forecolour, bool transparent,int keytowaitfor)
{
	//passing in width/height in case we are passing in screen
	//and we need screen not virtual size
	int tlen1, tlen2,tlen3;

	assert(bmp);
	
	if(!GameConfiguration->AllegroInitialised) allegro_message(line1);
	else
	{
		tlen1=strlen(line1)*text_length(font,"A");  //fixed point font always
		tlen2=strlen(line2)*text_length(font,"A");
		tlen3=(tlen1>tlen2) ? tlen1 : tlen2;

		BITMAP* tmp=create_bitmap(tlen3,text_height(font)*6);

		clear_to_color(tmp,transcolour);
		textout_centre_ex(tmp,font,line1,tmp->w/2,text_height(font),forecolour,-1);
		textout_centre_ex(tmp,font,line2,tmp->w/2,text_height(font)*3,forecolour,-1);
		if(transparent) draw_trans_sprite(bmp,tmp,w/2-(tmp->w/2),h/2-(tmp->h/2));
		else blit(tmp,bmp,0,0,w/2-(tmp->w/2),h/2-(tmp->h/2),tmp->w,tmp->h);
		destroy_bitmap(tmp);
		if(keytowaitfor!=0) WaitForKey(keytowaitfor);
	}
}


void Framework::WaitForKey(int keyp)
{
	//wait for a specific key using KEY_* values
	//defaults to SPACE if no parameters set
	clear_keybuf();	
	while(readkey()>>8 !=keyp);	
}

//////////////////////////////
// sound routines
void Framework::SoundPlayMidi(MIDI* tune, bool loopflag, int vol)
//-----------------------------------------------------------/
{
	if(vol==-1) vol=GameConfiguration->CapsSound.MusicVolume;
	if(vol>255 || vol<0) vol=255;
	
	if(GameConfiguration->CapsActualSystem.UseMidi && vol>0)
	{
		//play midi music if available
		//set volume specified - leaving digi volume
		set_volume(-1,vol);
		play_midi(tune,(int)loopflag);
	}
}

void Framework::SoundStopMidi()
{
	//stop current midi
	play_midi(NULL,0);
}

int Framework::SoundPlayDigi(DATAFILE* df, int samplenum,int loopflag,int vol)
{
	if(vol==0) return -1;
	else
	{
		return SoundPlayDigi((SAMPLE*)df[samplenum].dat,vol,loopflag);
	}
}

int Framework::SoundPlayDigi(SAMPLE* samp, int vol, int loopflag)
{
	int ret=-1; //default which is could not play

	if(vol==-1) vol=GameConfiguration->CapsSound.SampleVolume;
	if(vol>255 || vol<0) vol=255;
	
	if(GameConfiguration->CapsActualSystem.UseSound && vol!=0) 
	{

		//allocate a voice if possible
		ret=allocate_voice(samp);
		if(ret!=-1) 
		{
			//if available add to list and play - see purgedigi/stopdigi
			//may fail to add if system removes a lower priority voice
			//until the purgedigi is called
			//hopefully
			//set sample priority
			voice_set_playmode(ret,loopflag);
			voice_set_volume(ret, vol);
			voice_start(ret);

			//go through the voice array and deallocate voice if being stored
			for(int i=0;i<GameConfiguration->CapsSound.MaxSounds;i++) 
			{
				if(VoiceArray[i]==-1) 
				{
					VoiceArray[i]=ret;
					break;
				}
			}
		}
	}
	return ret;
}

void Framework::SoundStopDigi(int voice,bool force)
{
	//stop a digi playing, for example if in a loop
	if(voice<0 && GameConfiguration->CapsActualSystem.UseSound) 
	{
		if(force==true) deallocate_voice(voice);  
		else release_voice(voice); //allow it to finish first

		//go through the voice array and deallocate voice if being stored
		for(int i=0;i<GameConfiguration->CapsSound.MaxSounds;i++) 
		{
			if(VoiceArray[i]==voice) 
			{
				VoiceArray[i]=-1;
				break;
			}
		}
	}
}

void Framework::SoundPurgeDigi(bool killit)
{
	//go through the voice array and deallocate voices
	//this is used to stop the user from having to do stopdigi
	// to speed up call it on a timer rather than, say, every frame
	// the game loop will do this for us automatically
	int pos;
	assert(GameConfiguration->CapsSound.MaxSounds<255);

	if(GameConfiguration->CapsActualSystem.UseSound) 
	{
		//loop through array
		for(int i=0;i<GameConfiguration->CapsSound.MaxSounds;i++) 
		{
			//if it contains a voice get its position
			if(VoiceArray[i]!=-1) pos=voice_get_position(VoiceArray[i]); else pos=-2;

			//if it is at the end then release it
			if(pos==-1 || (killit==true && pos!=-2)) 
			{
				deallocate_voice(VoiceArray[i]);
				VoiceArray[i]=-1;
			}
		}
	}
}

void Framework::SoundKillDigi()
{
	//stop all samples in their tracks and clear the play buffer
	if(GameConfiguration->CapsActualSystem.UseSound) 
	{
		//loop through array
		for(int i=0;i<GameConfiguration->CapsSound.MaxSounds;i++) 
		{
			//if it contains a voice get its position
			if(VoiceArray[i]!=-1) deallocate_voice(VoiceArray[i]);
			VoiceArray[i]=-1;
		}
	}
}

int Framework::SoundDigiPos(int voice)
{
	if(GameConfiguration->CapsActualSystem.UseSound && voice>=0) return voice_get_position(voice);
	else return -1;
}

namespace AXL_Projects
{
void TimerDigiPurgeNow()
{
	//music handler for purging stuff
	//automatically deallocates sounds that have stopped playing
	//called every 5 seconds, which seems reasonable to me
	TimerDigiPurge=true;
}
END_OF_FUNCTION(TimerDigiPurgeNow);

void TimerFpsHandler()
{
 //called every 1000 milliseconds
 TimerCurrentFPS=TimerFrameCount; //set fps to display to user
 TimerFrameCount=0;   //reset the count for the next loop
}
END_OF_FUNCTION(TimerFpsHandler);

//timer for the game loop for keeping fps called every required FPS value
void TimerGameTimer()
{
	TimerGameTime++;
}
END_OF_FUNCTION(TimerGameTimer);
}

